package sys_ctrl

import (
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"
	"ubt-aio-service/app/mod/sys_mod"
	"ubt-aio-service/app/pkg/code"
	"ubt-aio-service/app/pkg/conf"
	"ubt-aio-service/app/pkg/logger"
	"ubt-aio-service/app/pkg/parse"
	"ubt-aio-service/app/serv/sys_serv"
	"ubt-aio-service/app/utils"
)

type sysFile struct{}

var SysFileCtrl sysFile

// Upload 上传文件，保存至数据库中，进行用户校验
func (c *sysFile) Upload(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	//从表单中读取文件
	file, header, err := r.FormFile("upFile")
	if err != nil {
		logger.Error("文件获取失败。" + err.Error())
		utils.ToJson(w, utils.UpfileErr)
		return
	}
	var fileName, extName, dateStr, basePath, baseUrl, filePath, srcPath, thumbPath, vstUrl, thumbUrl string
	basePath = conf.Cfg.AppConf.FilePath + "/file/"
	baseUrl = conf.Cfg.AppConf.FileUrl + "/file/"
	dateStr = time.Now().Format("200601")
	fileName = header.Filename
	extName = path.Ext(fileName)
	extName = strings.ToLower(extName)
	fileName = fileName[:len(fileName)-len(extName)]
	var maxSize int64 = 500 * 1024 * 1024
	var kind uint8
	if !strings.Contains(".jpg.png.gif.bmp.jpeg.txt.xls.xlsx.doc.docx.pdf.zip.7z.rar.mp3.wma.mp4.mpeg", extName) {
		utils.ToJson(w, utils.NotAllow)
		return
	}
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		kind = 1
		maxSize = 5 * 1024 * 1024
		filePath = basePath + "img/src/" + dateStr + "/"
		srcPath = basePath + "img/" + dateStr + "/"
		thumbPath = basePath + "img/thumb/" + dateStr + "/"
		vstUrl = baseUrl + "img/" + dateStr + "/"
		thumbUrl = baseUrl + "img/thumb/" + dateStr + "/"
		// 创建源文件的目录
		if _, err = os.Stat(srcPath); err != nil || os.IsNotExist(err) {
			if err = os.Mkdir(srcPath, os.ModePerm); err != nil {
				logger.Error("上传目录创建失败。" + err.Error())
				utils.ToJson(w, utils.UpPathErr)
				return
			}
		}
		// 创建缩略图的目录
		if _, err = os.Stat(thumbPath); err != nil || os.IsNotExist(err) {
			if err = os.Mkdir(thumbPath, os.ModePerm); err != nil {
				logger.Error("上传目录创建失败。" + err.Error())
				utils.ToJson(w, utils.UpPathErr)
				return
			}
		}
	} else if strings.Contains(".xls.xlsx.doc.docx.pdf", extName) {
		kind = 2
		filePath = basePath + "doc/" + dateStr + "/"
		vstUrl = baseUrl + "doc/" + dateStr + "/"
	} else if strings.Contains(".zip.7z.rar", extName) {
		kind = 3
		filePath = basePath + "zip/" + dateStr + "/"
		vstUrl = baseUrl + "zip/" + dateStr + "/"
	} else if strings.Contains(".mp3.wma.mp4.mpeg", extName) {
		kind = 4
		maxSize = 500 * 1024 * 1024
		filePath = basePath + "media/" + dateStr + "/"
		vstUrl = baseUrl + "media/" + dateStr + "/"
	} else {
		kind = 5
		filePath = basePath + "other/" + dateStr + "/"
		vstUrl = baseUrl + "other/" + dateStr + "/"
	}
	if maxSize < header.Size {
		utils.ToJson(w, utils.FileSize)
		return
	}
	// 创建目录
	if _, err = os.Stat(filePath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(filePath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson(w, utils.UpPathErr)
			return
		}
	}
	//重命名文件
	newName := time.Now().Format("02150405") + fmt.Sprintf("%08v", rand.Intn(100000000)) + extName
	newFile, err := os.Create(filePath + newName)
	if err != nil {
		logger.Error("文件上传失败。" + err.Error())
		utils.ToJson(w, utils.UploadErr)
		return
	}
	//将文件写到本地
	if _, err = io.Copy(newFile, file); err != nil {
		logger.Error("文件保存失败。" + err.Error())
		utils.ToJson(w, utils.UploadErr)
		return
	}
	// 如果是图片格式的文件，则进行压缩和生成缩略图
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		// 压缩图片，高度设为0
		srcPath += newName
		utils.ImgProc.ThumbImage(filePath+newName, srcPath, 1080, 0)
		// 生成缩略图
		thumbPath += newName
		utils.ImgProc.ThumbImage(filePath+newName, thumbPath, 240, 240)
	}
	fileId := code.NewNanoid(21)
	// 将文件信息保存至数据库中
	req := &sys_mod.SysFileAddReq{
		FileId:   fileId,
		FileName: fileName,
		FileSize: header.Size,
		Kind:     kind,
		FileExt:  extName,
		RealPath: filePath + newName,
		VisitUrl: vstUrl + newName,
	}
	res := sys_serv.SysFileServ.Add(req)
	var fileInfo map[string]string = nil
	if res.Code == utils.Succeed.Code {
		fileInfo = make(map[string]string)
		fileInfo["id"] = fileId
		fileInfo["name"] = fileName
		fileInfo["url"] = vstUrl + newName
		fileInfo["thumb"] = thumbUrl + newName
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 1}
		sys_serv.SysLogServ.Add(logReq)
	}
	utils.ToJson(w, &utils.ResJson{Code: res.Code, Msg: res.Msg, Data: fileInfo})
}

// UpImg4We 上传文件
func (c *sysFile) UpImg4We(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson4We(w, &utils.RetJson{utils.NoLogin.Code, utils.NoLogin.Msg, nil})
		return
	}
	//从表单中读取文件
	file, header, err := r.FormFile("upImg")
	if err != nil {
		logger.Error("文件获取失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UpfileErr.Code, utils.UpfileErr.Msg, nil})
		return
	}
	var fileName, extName, dateStr, basePath, baseUrl, filePath, srcPath, thumbPath, vstUrl string
	basePath = conf.Cfg.AppConf.FilePath + "/file/"
	baseUrl = conf.Cfg.AppConf.FileUrl + "/file/"
	dateStr = time.Now().Format("200601")
	fileName = header.Filename
	extName = path.Ext(fileName)
	extName = strings.ToLower(extName)
	fileName = fileName[:len(fileName)-len(extName)]
	var maxSize int64 = 5 * 1024 * 1024
	var kind uint8
	if !strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		utils.ToJson4We(w, &utils.RetJson{utils.NotAllow.Code, utils.NotAllow.Msg, nil})
		return
	}
	kind = 1
	filePath = basePath + "img/src/" + dateStr + "/"
	srcPath = basePath + "img/" + dateStr + "/"
	thumbPath = basePath + "img/thumb/" + dateStr + "/"
	vstUrl = baseUrl + "img/" + dateStr + "/"
	// 创建缩略图的目录
	if _, err = os.Stat(thumbPath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(thumbPath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson4We(w, &utils.RetJson{utils.UpPathErr.Code, utils.UpPathErr.Msg, nil})
			return
		}
	}
	if maxSize < header.Size {
		utils.ToJson4We(w, &utils.RetJson{utils.FileSize.Code, utils.FileSize.Msg, nil})
		return
	}
	// 创建目录
	if _, err = os.Stat(filePath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(filePath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson4We(w, &utils.RetJson{utils.UpPathErr.Code, utils.UpPathErr.Msg, nil})
			return
		}
	}
	// 创建源文件的目录
	if _, err = os.Stat(srcPath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(srcPath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson4We(w, &utils.RetJson{utils.UpPathErr.Code, utils.UpPathErr.Msg, nil})
			return
		}
	}
	//重命名文件
	newName := time.Now().Format("02150405") + fmt.Sprintf("%08v", rand.Intn(100000000)) + extName
	newFile, err := os.Create(filePath + newName)
	if err != nil {
		logger.Error("文件上传失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UploadErr.Code, utils.UploadErr.Msg, nil})
		return
	}
	//将文件写到本地
	if _, err = io.Copy(newFile, file); err != nil {
		logger.Error("文件保存失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UploadErr.Code, utils.UploadErr.Msg, nil})
		return
	}
	// 压缩图片，高度设为0
	srcPath += newName
	utils.ImgProc.ThumbImage(filePath+newName, srcPath, 1080, 0)
	// 生成缩略图
	thumbPath += newName
	utils.ImgProc.ThumbImage(filePath+newName, thumbPath, 240, 240)
	fileId := code.NewNanoid(21)
	req := &sys_mod.SysFileAddReq{
		FileId:   fileId,
		FileName: fileName,
		FileSize: header.Size,
		Kind:     kind,
		FileExt:  extName,
		RealPath: filePath + newName,
		VisitUrl: vstUrl + newName,
	}
	res := sys_serv.SysFileServ.Add(req)
	var fileInfo map[string]string = nil
	if res.Code == utils.Succeed.Code {
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 1}
		sys_serv.SysLogServ.Add(logReq)
		fileInfo = make(map[string]string)
		fileInfo["alt"] = fileName
		fileInfo["url"] = vstUrl + newName
	}
	utils.ToJson4We(w, &utils.RetJson{utils.Succeed.Code, utils.Succeed.Msg, fileInfo})
}

// Upvd4We 上传视频文件
func (c *sysFile) Upvd4We(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson4We(w, &utils.RetJson{utils.NoLogin.Code, utils.NoLogin.Msg, nil})
		return
	}
	//从表单中读取文件
	file, header, err := r.FormFile("upVideo")
	if err != nil {
		logger.Error("文件获取失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UpfileErr.Code, utils.UpfileErr.Msg, nil})
		return
	}
	var fileName, extName, dateStr, basePath, baseUrl, filePath, srcPath, vstUrl string
	basePath = conf.Cfg.AppConf.FilePath + "/file/"
	baseUrl = conf.Cfg.AppConf.FileUrl + "/file/"
	dateStr = time.Now().Format("200601")
	fileName = header.Filename
	extName = path.Ext(fileName)
	extName = strings.ToLower(extName)
	fileName = fileName[:len(fileName)-len(extName)]
	var maxSize int64 = 500 * 1024 * 1024
	var kind uint8
	if !strings.Contains(".mp4.mpeg", extName) {
		logger.Error("不允许上传的类型。")
		utils.ToJson4We(w, &utils.RetJson{utils.NotAllow.Code, utils.NotAllow.Msg, nil})
		return
	}
	kind = 4
	filePath = basePath + "media/" + dateStr + "/"
	vstUrl = baseUrl + "media/" + dateStr + "/"
	if maxSize < header.Size {
		utils.ToJson4We(w, &utils.RetJson{utils.FileSize.Code, utils.FileSize.Msg, nil})
		return
	}
	// 创建目录
	if _, err = os.Stat(filePath); err != nil || os.IsNotExist(err) {
		if err = os.Mkdir(filePath, os.ModePerm); err != nil {
			logger.Error("上传目录创建失败。" + err.Error())
			utils.ToJson4We(w, &utils.RetJson{utils.UpPathErr.Code, utils.UpPathErr.Msg, nil})
			return
		}
	}
	//重命名文件
	newName := time.Now().Format("02150405") + fmt.Sprintf("%08v", rand.Intn(100000000)) + extName
	newFile, err := os.Create(filePath + newName)
	if err != nil {
		logger.Error("文件上传失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UploadErr.Code, utils.UploadErr.Msg, nil})
		return
	}
	//将文件写到本地
	if _, err = io.Copy(newFile, file); err != nil {
		logger.Error("文件保存失败。" + err.Error())
		utils.ToJson4We(w, &utils.RetJson{utils.UploadErr.Code, utils.UploadErr.Msg, nil})
		return
	}
	// 如果是图片格式的文件，则进行压缩和生成缩略图
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		// 压缩图片，高度设为0
		srcPath += newName
		utils.ImgProc.ThumbImage(filePath+newName, srcPath, 1080, 0)
		// 生成缩略图
	}
	fileId := code.NewNanoid(21)
	req := &sys_mod.SysFileAddReq{
		FileId:   fileId,
		FileName: fileName,
		FileSize: header.Size,
		Kind:     kind,
		FileExt:  extName,
		RealPath: filePath + newName,
		VisitUrl: vstUrl + newName,
	}
	res := sys_serv.SysFileServ.Add(req)
	var fileInfo map[string]string = nil
	if res.Code == utils.Succeed.Code {
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 1}
		sys_serv.SysLogServ.Add(logReq)
		fileInfo = make(map[string]string)
		fileInfo["alt"] = fileName
		fileInfo["url"] = vstUrl + newName
	}
	utils.ToJson4We(w, &utils.RetJson{utils.Succeed.Code, utils.Succeed.Msg, fileInfo})
}

// Deletes 批量删除文件
func (c *sysFile) Deletes(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	req := &sys_mod.SysFileProReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.FileIds = utils.InputFilter(req.FileIds, true)
	res := sys_serv.SysFileServ.Deletes(req.FileIds)
	if res.Code == utils.Succeed.Code {
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 2}
		sys_serv.SysLogServ.Add(logReq)
	}
	utils.ToJson(w, res)
}

// Download 下载文件
func (c *sysFile) Download(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	req := &sys_mod.SysFileDownReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.FileId = utils.InputFilter(req.FileId, true)
	res := sys_serv.SysFileServ.GetRealPath(req.FileId)
	if res.Code == utils.Succeed.Code {
		realPath, fileName := res.Data.(map[string]any)["RealPath"].(string), res.Data.(map[string]any)["FileName"].(string)
		if _, err := os.Stat(realPath); err != nil || os.IsNotExist(err) {
			utils.ToJson(w, utils.DownloadErr)
			return
		}
		fb, err := os.ReadFile(realPath)
		if err != nil {
			utils.ToJson(w, utils.DownloadErr)
			return
		}
		w.Header().Add("content-type", "application/octet-stream")
		w.Header().Add("content-disposition", "attachment; filename="+url.QueryEscape(fileName))
		w.Header().Add("Access-Control-Expose-Headers", "content-disposition")
		if _, err = w.Write(fb); err != nil {
			utils.ToJson(w, utils.DownloadErr)
		}
	}
	utils.ToJson(w, utils.SysErr)
}

// UpdName 修改文件名称
func (c *sysFile) UpdName(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	req := &sys_mod.SysFileNameReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.FileId = utils.InputFilter(req.FileId, true)
	req.FileName = utils.InputFilter(req.FileName, true)
	res := sys_serv.SysFileServ.UpdName(req)
	if res.Code == utils.Succeed.Code {
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 3}
		sys_serv.SysLogServ.Add(logReq)
	}
	utils.ToJson(w, res)
}

// UpdType 修改文件信息
func (c *sysFile) UpdType(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	req := &sys_mod.SysFileTypeReq{}
	// 将请求的参数转换为请求结构体
	if err := parse.Parse(r, req); err != "" {
		utils.ToJson(w, &utils.ResJson{Code: utils.ParamErr.Code, Msg: err})
		return
	}
	// 过滤关键参数值中的非法字符
	req.FileIds = utils.InputFilter(req.FileIds, true)
	req.FileType = utils.InputFilter(req.FileType, true)
	res := sys_serv.SysFileServ.UpdType(req)
	if res.Code == utils.Succeed.Code {
		logReq := &sys_mod.SysLogSaveReq{UserId: userInfo.UserId, MenuCode: utils.SysFile, IpAddr: userInfo.ClientIp, Kind: 3}
		sys_serv.SysLogServ.Add(logReq)
	}
	utils.ToJson(w, res)
}

// One 获取文件信息详情
func (c *sysFile) One(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	// 过滤关键参数值中的非法字符
	u := r.URL.Query()
	if u == nil {
		utils.ToJson(w, utils.ParamErr)
		return
	}
	fileId := u.Get("fileId")
	fileId = utils.InputFilter(fileId, true)
	if fileId == "" || len(fileId) != 21 {
		utils.ToJson(w, utils.ParamErr)
		return
	}
	res := sys_serv.SysFileServ.One(fileId)
	utils.ToJson(w, res)
}

// Page 获取文件分页列表
func (c *sysFile) Page(r *http.Request, w http.ResponseWriter) {
	// 通过头部请求的token获取用户信息
	token := r.Header.Get(utils.Token)
	userInfo := sys_serv.GetUserInfo(token)
	if userInfo == nil {
		utils.ToJson(w, utils.NoLogin)
		return
	}
	// 过滤关键参数值中的非法字符
	u := r.URL.Query()
	if u == nil {
		utils.ToJson(w, utils.ParamErr)
		return
	}
	keywords := u.Get("keywords")
	fileType := u.Get("fileType")
	kinds := u.Get("kind")
	beginTimes := u.Get("beginTime")
	endTimes := u.Get("endTime")
	limits := u.Get("limit")
	offsets := u.Get("offset")
	keywords = utils.InputFilter(keywords, false)
	fileType = utils.InputFilter(fileType, true)
	kinds = utils.InputFilter(kinds, true)
	beginTimes = utils.InputFilter(beginTimes, true)
	endTimes = utils.InputFilter(endTimes, true)
	limits = utils.InputFilter(limits, true)
	offsets = utils.InputFilter(offsets, true)
	kind := parse.Str2u8(kinds)
	beginTime := parse.Str2u32(beginTimes)
	endTime := parse.Str2u32(endTimes)
	limit := parse.Str2u8(limits)
	offset := parse.Str2u32(offsets)
	// 先统计信息的数量
	req := &sys_mod.SysFilePageReq{Keywords: keywords, FileType: fileType, Kind: kind, BeginTime: beginTime, EndTime: endTime}
	res := sys_serv.SysFileServ.Count(req)
	if res.Code == utils.Succeed.Code {
		dto := &sys_mod.SysFilePageRes{Total: res.Data.(map[string]any)["nums"].(int64)}
		// 再查询具体的信息列表
		req.Limit = limit
		req.Offset = offset
		if res = sys_serv.SysFileServ.Page(req); res.Code == utils.Succeed.Code {
			dto.List = res.Data
			utils.ToJson(w, &utils.ResJson{Code: res.Code, Msg: res.Msg, Data: dto})
			return
		}
	}
	utils.ToJson(w, utils.NoData)
}
